#!/usr/bin/env python2.5

## Usage:  

## Determines, using WordNet, the tag of a given word.
## This will only be used in cases wherein the default tag is applied (usu. "NN").

import nltk, re, sys
from nltk import corpus, tag
from nltk.corpus import wordnet as WN
from nltk.tag import sequential 
from nltk.tag.sequential import *

import tokenize

nltk.data.path.append('/corpora/nltk/nltk-data/')

class JoshTagger(DefaultTagger):
	"""Overwrites the DefaultTagger class to use my Morphy-based tagger."""

	def choose_tag(self, tokens, index, history):
		"""Tags a word which had heretofore received the default tag (usu. "NN") by using WordNet."""

		## Possible tags:
		##     Noun:  NN, NNS, NNP, NNPS
		##     Verb:  VB, VBZ, VBD, VBG, VBP, VBN
		##     Adjective:  JJ, JJR, JJS
		##     Adverb:  RB, RBR, RBS
		##     Other:  CD, LS, UH, SYM

		word = tokens[index]
		if (index > 1):	prevtag = history[index - 1]
		else:	prevtag = None
		pos_poss = dict()
	
		## First, we look for its entry on WordNet (not panicking if not present).
		if word.isdigit():	return "CD"
		if not word.isalnum():	return "SYM"
		nouns = len(WN.synsets(word, pos = WN.NOUN))
		verbs = len(WN.synsets(word, pos = WN.VERB))
		adjs = len(WN.synsets(word, pos = WN.ADJ))
		advs = len(WN.synsets(word, pos = WN.ADV))
		if nouns:
			pos_poss["NN"] = 1
			if (word.istitle()):	pos_poss["NNP"] = 1
			if (word[-1] == "s"):	pos_poss["NNS"] = 1
			if (word.istitle() and word[-1] == "s"):	pos_poss["NNPS"] = 1
		if verbs:	
			pos_poss["VBD"] = 1
			pos_poss["VBP"] = 1
			if (prevtag == "TO" or not prevtag):	pos_poss["VB"] = 1
			if prevtag == "VBG":	pos_poss["VBN"] = 1
			if (word[-1] == "s"):	pos_poss["VBZ"] = 1
			if (word[-3:] == "ing"):	pos_poss["VBG"] = 1
		if advs:	
			pos_poss["RB"] = 1
			if (word[-2:] == "er"):	pos_poss["RBR"] = 1
			if (word[-3:] == "est"):	pos_poss["RBS"] = 1
		if 	adjs:	
			pos_poss["JJ"] = 1
			if (word[-2:] == "er"):	pos_poss["JJR"] = 1
			if (word[-3:] == "est"):	pos_poss["JJS"] = 1
	
		## Then, we stem it, and compare it to the original word to discover the transformation.
		baseN = WN.morphy(word, WN.NOUN)
		baseV = WN.morphy(word, WN.VERB)
		baseA = WN.morphy(word, WN.ADV)
		baseJ = WN.morphy(word, WN.ADJ)
	
		##    Nouns
		if (baseN and baseN != word):
			if "NN" in pos_poss:	del pos_poss["NN"]
			if "NNP" in pos_poss:	del pos_poss["NNP"]
			## We'll check to see if it's a proper noun, though, or a common one, stemming it as necessary.
			if (WN.synsets(baseN.lower(), pos = WN.NOUN)):
				if "NNP" in pos_poss:	del pos_poss["NNP"]
				if "NNPS" in pos_poss:	del pos_poss["NNPS"]
			else:
				if "NN" in pos_poss:	del pos_poss["NN"]
				if "NNS" in pos_poss:	del pos_poss["NNS"]
	
			##    Adjectives
		if (baseJ and baseJ != word):
			if "JJ" in pos_poss:	del pos_poss["JJ"]
	
		##    Adverbs
		if (baseA and baseA != word):
			if "RB" in pos_poss:	del pos_poss["RB"]
	
		##    Verbs
		if (baseV and baseV != word):
			if "VB" in pos_poss:	del pos_poss["VB"]
			if "VBP" in pos_poss:
				if not ((word in ['are', 'am']) and baseV == "be"):	del pos_poss["VBP"]
			# if not ((baseV[-1] == "o" and word[-2:] == "ne") or (baseV[-1] != "o" and word[-1] == "n")):	del pos_poss["VBN"]
		
		## In the end, should we have two or more Penn tags for this word, split the tie by ranking them word according to its
		##   plurality of definitions in WordNet.
		# print "%s has %d uses as a noun, %d as a verb, %d as an adverb, and %d as an adjective." % (word, nouns, verbs, advs, adjs)
		prolif = max([advs, adjs, nouns, verbs])
		if (prolif == advs):
			if ("RBS" in pos_poss):	return "RBS"
			if ("RBR" in pos_poss): return "RBR"
			if ("RB" in pos_poss):	return "RB"
		if (prolif == adjs):
			if ("JJS" in pos_poss):	return "JJS"
			if ("JJR" in pos_poss): return "JJR"
			if ("JJ" in pos_poss):	return "JJ"
		if (prolif == verbs):
			if ("VBG" in pos_poss): return "VBG"
			if ("VBZ" in pos_poss):	return "VBZ"
			if ("VBN" in pos_poss):	return "VBN"
			if ("VB" in pos_poss):	return "VB"
			if ("VBP" in pos_poss): return "VBP"
			if ("VBD" in pos_poss):	return "VBD"
		if (prolif == nouns):
			if ("NNPS" in pos_poss):	return "NNPS"
			if ("NNP" in pos_poss): return "NNP"
			if ("NNS" in pos_poss):	return "NNS"
		
		## The default is returned, if nothing came up fitting.
		return "NN"

## End of class definition.

def getTagger(file, trial = 1, NUM_SENTS = 0, N = 4):
	"""Creates, trains, and returns a Sequential tagger."""

	if (trial == 1):	default_tagger = nltk.DefaultTagger("NN")
	if (trial == 2):	default_tagger = JoshTagger("NN")
	tagfile = open(file, 'r')
	sent = list()
	token_seq = list()
	tag_seq = list()
	training_data = list()
	RCE = 0
	for entry in tagfile:
		if ((NUM_SENTS != 0) and (NUM_SENTS == RCE)):	break
		if (entry == "\n"):
			training_data.append(sent)
			sent = list()
			continue
		entry = tokenize.tokenize(entry, 3).strip()
		sent.append(tuple([" ".join(entry.split()[:-1]), entry.split()[-1]]))
		token_seq.append(" ".join(entry.split()[:-1]))
		tag_seq.append(entry.split()[-1])
		RCE += 1
	uni = nltk.UnigramTagger(train=training_data, backoff=default_tagger)
	bi = nltk.BigramTagger(train=training_data, backoff=uni)
	tri = nltk.TrigramTagger(train=training_data, backoff=bi)
	return (nltk.NgramTagger(N, train=training_data, backoff=tri), training_data)

## Run a simple test.
if (__name__ == "__main__"):
	if len(sys.argv) > 2:	trial = int(sys.argv[2])
	else:	trial = 1
	(SeqTagger, train_data) = getTagger(sys.argv[1], trial)
	test = list(["Inertia is a property of the Mars 's matter .".split()])
	print SeqTagger.batch_tag(test)
	test = list(["That fungi on the ground are brown molds .".split()])
	print SeqTagger.batch_tag(test)
	print
	print "Accuracy:  %4.11f%%" % (100.0 * SeqTagger.evaluate(train_data))
	# print "The tag for " + sys.argv[1] + " is " + tagme(sys.argv[1], sys.argv[2]) + "."
